06. 合并冲突

有时候合并会失败

大部分情况下,git 将能够成功地合并分支。但是,有时候 git 无法完全自动地进行合并。合并失败时,就称为 合并冲突

如果出现合并冲突,git 将尝试尽可能合并多的内容,然后将留下特殊选项(例如 >>> <<< ),告诉你(没错,告诉作为程序员的你!)需要从何处手动修复。

什么导致了合并冲突

正如你所知道的,git 会跟踪文件中的代码行。如果完全相同的行在不同的文件中更改了,将产生合并冲突。例如,如果你在 alternate-sidebar-style 分支上并将侧栏的标题改为"Information About Me",git 应该选择哪个标题?你在两个分支上都更改了标题,因此 git 根本不知道你要保留哪个标题。它肯定不会随机选择一个标题!

我们来人为制造一个合并冲突,学习如何解决这种冲突。当你学会了后,就非常简单!当 git 不确定你要使用即将合并的分支中的哪些行时,就会出现合并冲突。因此我们需要在两个不同的分支上修改同一行,然后重设合并它们。

人为制造合并冲突!

当同一行在两个分支中都更改了时,就会出现合并冲突。我们在两个不同的分支上更改同一页面的标题:

  • 更改 master 分支上的标题
  • 在最近修改 master 分支的 commit 前面创建一个 heading-update 分支
  • 更改同一标题
  • 切换到 master 分支
  • 合并 heading-update 分支

在分支上更改标题 1

因为 master 分支就像所有其他分支一样,也是个普通分支,我们在 master 分支上更改标题。将 <h1> 标题改为其他内容。对我来说,标题当前在第 13 行是"Adventure",我将其改为"Quest"。

做出更改后,保存文件并 commit 到仓库。

在分支上更改标题 2

现在我们需要创建不同的分支并在该分支上更新标题。

现在要注意的是,我们需要创建一个不是从 master 分支上分叉的分支。如果我们在从 master 分支上分叉的分支上做出更改,那么该更改将在此更改前面,git 将直接使用该更改,而不是使用我们刚刚在 master 上做出的更改。因此我们需要将该分支“放在过去”。

我们创建一个位于最近 commit 之前的 commit 上的分支。使用 git log 获取上一个 commit 的 SHA,并在该 commit 上创建一个分支。在创建 heading-update 分支后,我的 git log 输出结果如下所示:

_终端显示了 `git log --oneline --decorate --graph --all` 的输出结果。当前被检出的分支为 `heading-update` 。_

终端显示了 git log --oneline --decorate --graph --all 的输出结果。当前被检出的分支为 heading-update

现在已经创建好分支,我们只需再次更新标题。确保更改在 master 分支上做出更改的同一行。我将第 13 行的"Adventure"改为"Crusade"。

然后保存文件,并将文件 commit 到仓库。

_终端显示了 `git log --oneline --decorate --graph --all` 的输出结果。`master` 分支和 `heading-update` 分支都更改了同一文件的同一行。_

终端显示了 git log --oneline --decorate --graph --all 的输出结果。 master 分支和 heading-update 分支都更改了同一文件的同一行。

准备合并冲突

Task Description:

我们即将导致合并冲突,确保你的进度和我的一样:

Task List:

Task Feedback:

很棒!我们继续。

确保你位于 master 分支(我们也可以在另一分支上操作,但是我习惯将 master 分支作为主分支,让另一个分支合并到该分支上)上,并合并 heading-update 分支:

$ git merge heading-update

你应该看到以下结果:

_终端显示了导致合并冲突的合并输出结果。出现单词"CONFLICT",并且指出 index.html 文件中出现合并冲突。_

终端显示了导致合并冲突的合并输出结果。出现单词"CONFLICT",并且指出 index.html 文件中出现合并冲突。

合并冲突输出结果解释

终端中显示的输出结果为:

$ git merge heading-update 
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.

注意在 git merge heading-update 命令之后,git 尝试合并在两个分支上都更改了的文件 ( index.html ),但是出现冲突。此外,它告诉你发生了什么:"Automatic merge failed; fix conflicts and then commit the result"。

还记得 git status 命令吗?在处理合并冲突时,该命令将非常有用。

合并冲突的状态?

尝试立即运行 git status 。终端显示了以下哪些信息?

SOLUTION:
  • You have unmerged paths
  • Unmerged paths
  • fix conflicts and run "git commit"
  • use "git add ..." to mark resolution

git status 的输出结果告诉我们 index.html 中存在合并冲突。因此在代码编辑器中查看该文件!

_代码编辑器显示了合并冲突指示符。_

代码编辑器显示了合并冲突指示符。

合并冲突指示符解释

编辑器具有以下合并冲突指示符:

  • <<<<<<< HEAD 此行下方的所有内容(直到下个指示符)显示了当前分支上的行
  • ||||||| merged common ancestors 此行下方的所有内容(直到下个指示符)显示了原始行的内容
  • ======= 表示原始行内容的结束位置,之后的所有行(直到下个指示符)是被合并的当前分支上的行的内容
  • >>>>>>> heading-update 是要被合并的分支(此例中是 heading-update 分支)上的行结束指示符

解决合并冲突

git 使用合并冲突指示符来告诉你两个不同分支上的哪些行导致了合并冲突,以及原始行是什么。要解决合并冲突,你需要:

  1. 选择保留哪些行
  • 删掉所有带指示符的行

因为某种原因,我不太喜欢现在的"Crusade"一词,但是"Quest"也不太合适。要不将标题设为"Adventurous Quest"?

_代码编辑器显示合并冲突指示符已被删除,并且标题已更新。_

代码编辑器显示合并冲突指示符已被删除,并且标题已更新。

commit 合并冲突

删掉所有包含合并冲突指示符的行并选择保留哪个标题后,直接保存文件,并将其添加到暂存区,然后 commit!就像普通合并一样,代码编辑器会弹出,并让你提供 commit 消息。和之前一样,我们经常会使用自动生成的合并 commit 消息,因此在编辑器打开后,直接关闭编辑器并使用自动生成的 commit 消息。

就这些内容!当你明白合并指示符所显示的内容后,合并冲突就没那么复杂了。

合并冲突指示符可以被 commit 吗?

你到目前为止已经提交了大量的 commit。如果文件中出现合并冲突,你修改并保存了文件,然后添加到暂存区并 commit 了,但是忘了删掉合并冲突指示符,git 会 commit 文件吗?

SOLUTION:

合并冲突小结

当相同的行在要合并的不同分支上做出了更改时,就会出现合并冲突。git 将在合并途中暂停,并告诉你存在冲突,以及哪些文件存在冲突。要解决文件中的冲突:

  • 找到并删掉存在合并冲突指示符的所有行
  • 决定保留哪些行
  • 保存文件
  • 暂存文件
  • 提交 commit

注意一个文件可能在多个部分存在合并冲突,因此检查整个文件中的合并冲突指示符,搜索 <<< 能够帮助你找到所有这些指示符。

深入研究